%% Initialization
clear

% adBudget = 2 ; %numLinks = 2;

params{1} = struct('neighbourhoodRadius',3,'numInitAgents',30,'numAgents',0,'adBudget',2 ,'numAds',2,'numTotal',0,'numMaxHierarchy',10,'maxNumNeighborhood',100);
simParams = struct('numItr',1000,'numRun',6);


% pl1=zeros(simParams.numRun, simParams.numItr);
% pl2=zeros(simParams.numRun, simParams.numItr);
% pl3=zeros(simParams.numRun, simParams.numItr);
% pl4=zeros(simParams.numRun, simParams.numItr);
% pl5=zeros(simParams.numRun, simParams.numItr);

iRun  = 0 ;

listOfInfluentialNodes{1} = cell(params{1}.numAds,1);

% NewTags=[];

%% Generate Connectivity Network
ld = 0.9; %0.1:0.1:0.8;   % the parameter for link density
dh = 0.8;        % the paramenter determines the disassortativity (0~0.5)----contrast to homophily
attrNoise = 0.2;
numObjs = 5;
attrSize = 10;
numAct = 1;  % for the 'power-law' synthetic generator,
numGroups = 4;

% Generate the Network and Labels
[Net,Attributes,labelVec] = NetGeneration(params{1}.numInitAgents,ld,dh,numGroups,attrNoise,numObjs,attrSize,numAct);
% Net  = round(100*rand(params{1}.numInitAgents));
% Check for low connectivity nodes (low inWeight and outWeight agents)
ind1_Net=find(sum(Net,1)<=0);
ind2_Net = find(sum(Net,2)<=0);
indFin_Net = union (ind1_Net, ind2_Net);

% Delete the nodes with low cconnectivity
Net(:,indFin_Net)=[];
Net(indFin_Net,:)=[];


% New Number of Agents
params{1}.numAgents = size(Net,1);
params{1}.numTotal = params{1}.numAgents + params{1}.numAds;
% Build graphConnectivity (Adjacency matrix) based on Net
graphConnectivity = zeros (params{1}.numAgents);
graphConnectivity(Net~=0)=1 ;

Net =sparse(Net);
graphConnectivity = sparse(graphConnectivity);

%% Generate matrix and connectivities of the network

% Generate agent(s)
agent{1} = InitiateAgents(Net,graphConnectivity,params{1});
agent1= agent{1};
save agent agent1

% Generate matrix parameters based on Net
matrixParams{1}= GenerateMatrixParameters(params{1},agent{1},Net,graphConnectivity);


% Generate matrix M
M = generate_M (params{1}.numAds);


%% Main Algorithm

%load TestNet
% counter/iterator for the hierarchy
iH = 1;
stopCriteria = true;
numTags = 0;
while (stopCriteria)
    
    if iH ==1
        prev_params = params{iH};
        prev_matrixParams = matrixParams{iH};
        prev_agent = agent{iH};
        prev_listOfInfluentialNodes = listOfInfluentialNodes{iH};
    else
        prev_params = params{iH-1};
        prev_matrixParams = matrixParams{iH-1};
        prev_agent = agent{iH-1};
        prev_listOfInfluentialNodes = listOfInfluentialNodes{iH-1};
    end;
    fprintf('Starting Heirarchy %d\r\n',iH);
    [matrixParams{iH},agent{iH},params{iH},numInfluentialNodes] = UpdateHeirarchy(prev_listOfInfluentialNodes,prev_matrixParams,prev_agent,prev_params);
    
    %   for each node do the following:
    for iNode = 1:params{iH}.numAgents
        fprintf('\tHeirarchy %d,Node %d/%d, #influentials %d, #tags %d\r\n',iH,iNode,params{iH}.numAgents, numInfluentialNodes,numTags);
        %       % Find the neighbourhood N and the Boundry nodes ID
        [neighbourhoodList,boundaryNodesID] = FindNeighbourhood(agent{iH},iNode,params{iH});
        
        % Make the subgraph from the neighbourhoodList
        [subMatrixParams,subgraphParams] = MakeSubgraph(matrixParams{iH},neighbourhoodList,params{iH});
        %
        %       % Add outside world effect ( adding the virtual nodes to the subgraph)
%         [augmentedSubMatrixParams,subgraphParams] = AddOutsideWorldEffect(matrixParams{iH},subMatrixParams,agent{iH},neighbourhoodList,boundaryNodesID,subgraphParams);
        
        %       Tag the influential nodes in N
        %       1) Build Q matrix
%         [Q_mat,A,B_coef,W_coef,HasInverse] = GenerateQ(subgraphParams,M,augmentedSubMatrixParams);
        
                [Q_mat,A,B_coef,W_coef,HasInverse] = GenerateQ(subgraphParams,M,subMatrixParams);
        
        if ~HasInverse
            
            continue;
        end;
        %       2) Solve the optimization problem
        %         [tags,U_opt]= tagInfluentialNodes(W_coef,A,augmentedSubMatrixParams,subgraphParams);
        [tags,U_opt]= tagInfluentialNodes(W_coef,A,subMatrixParams,subgraphParams);
        numTags = sum(tags(:)>0);
        
        % Update agent Influence tags InfTag ( don't use the virtual nodes)
        agent{iH} = UpdateAgentInfTag(agent{iH},tags,neighbourhoodList);
    end %end for each node
    
    
    % finds the list of influential nodes based on InfTags of agent
    % influential nodes are those nodes that are at lease selected once
    % the content is the ID of the node in the current hierarchy
    listOfInfluentialNodes{iH} = GetInfluentialNodes(agent{iH},params{iH});
    
    listOfInfluentialNodesPrev=[];
    if iH>1
        listOfInfluentialNodesPrev =  listOfInfluentialNodes{iH-1};
    end
    stopCriteria = StoppingCriteria(listOfInfluentialNodes{iH},listOfInfluentialNodesPrev,params{iH},iH);
    if (~stopCriteria)
        fprintf('reached the stopping condition\r\n')
    end;
    listOfNodes = unique([listOfInfluentialNodes{iH}{:}]);
    numInfluentialNodes = length(listOfNodes);
    iH  = iH + 1 ;
end %END while


%Build the U_opt matrix which is a connection matrix from Ads to Agents.
U_FinalOpt = BuildAdAgentConnection (listOfInfluentialNodes{iH-1},agent{iH-1},params{1});


%% Other Algorithms

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Calculate Old Optimization U
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% [Q_mat,A,B_coef,W_coef,HasInverse] = GenerateQ(params{1},M,matrixParams{1});

% [tags,U_OldOpt]= tagInfluentialNodes(W_coef,A,matrixParams{1},params{1});
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Calculate Random U
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

U_rnd = zeros(params{1}.numAds, params{1}.numAgents);
for iAds = 1:params{1}.numAds
    tmp_rnd = randperm(params{1}.numAgents);
    SeedNodes_rnd = tmp_rnd(1:params{1}.adBudget);
    U_rnd(iAds, SeedNodes_rnd)= 1;
    %     id = i+ numAgents;
    %     agent(id).neighbors = SeedNodes_rnd ;
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Calculate Degree_based U (PREFERENTIAL ATTACHMENT)
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

U_deg = zeros(params{1}.numAds, params{1}.numAgents);
P_attach = [agent{1}(1:params{1}.numAgents).outWeight] ./ sum([agent{1}(1:params{1}.numAgents).outWeight]) ;
rollet_deg = cumsum (P_attach);
for iAds = 1:params{1}.numAds
    l=0;
    while (l ~= params{1}.adBudget)
        tmp_deg = find (rollet_deg >= rand);
        idLink = tmp_deg(1);
        if  (U_deg(iAds ,idLink)==0)
            l = l+1 ;
            U_deg (iAds, idLink ) = 1;
        else
            continue;
        end % IF
        
    end % WHILE
end % FOR iAds
% [val, ind]= sort([agent.outDegree]);
% SeedNodes_deg = ind (1:numLinks);


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Calculate PageTank U
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Run the simulation

%%%%%%%%%%%%%%%%%%%%%%%%%%%
%RESULT INITIALIZATION
%%%%%%%%%%%%%%%%%%%%%%%%%%%
figure(2)
% Set different methods
%     Method 1 ---- Random
%     Method 2 ---- Degree Based
%     Method 3 ---- Betweenness
%     Method 4 ---- Our Optimization
%     Method 5 ---- Old Optimization
Method = [1 2 4 ];% 5];
for iRun = 1:simParams.numRun
    
    for iMethod = 1:length(Method)
        load agent
        agentItr{iMethod}=agent1 ;
        Results{iRun}(iMethod).ItrAve = zeros(params{1}.numAds,simParams.numItr);
        Results{iRun}(iMethod).FinalDesireVec = [];
        Results{iRun}(iMethod).AveFinalDesireVec = [] ;
    end
    
    %%%%%%%%%%%%%%%%%%%%%%%%%%%
    %ITERATION START
    %%%%%%%%%%%%%%%%%%%%%%%%%%%
    for itr = 1:simParams.numItr
        if mod(itr,10) ==1
            fprintf('Run %d/%d, iteration %d/%d\r\n',iRun,simParams.numRun,itr,simParams.numItr);
        end;
        id1 = randi(params{1}.numTotal,1);
        
        if id1<= params{1}.numAgents      % The selected agent is Regular agent
            
            %Select the other agent to interact with
            P_interaction = matrixParams{1}.P_agents (id1,:);
            rollet = cumsum (P_interaction);
            %TODO: Does it make any difference? Should I write it in another way?
            tmp = find (rollet >= rand);
            id2= tmp(1);
            
            %Sharing the ideas
            if rand<= matrixParams{1}.Alpha_agents(id1,id2)
                for iMethod= 1:length(Method)
                    agentItr{iMethod}(id1).desireVec = matrixParams{1}.Eps_agents (id1,id2)*(M*agentItr{iMethod}(id1).desireVec) + (1-matrixParams{1}.Eps_agents (id1,id2)) * (M*agentItr{iMethod}(id2).desireVec);
                    agentItr{iMethod}(id2).desireVec = agentItr{iMethod}(id2).desireVec;
                end
                %TODO: Is it possible to write the same formula for Agent(id2) desire vector? Does it contrast to the formula you had before?
            else
                for iMethod= 1:length(Method)
                    agentItr{iMethod}(id1).desireVec = agentItr{iMethod}(id1).desireVec;
                    agentItr{iMethod}(id2).desireVec = agentItr{iMethod}(id2).desireVec;
                end
            end % IF rand
            
            %%%%%%%%%%%%%%%%%%%%%%%%%%%
            %RESULT SAVE
            %%%%%%%%%%%%%%%%%%%%%%%%%%%
            for iMethod= 1:length(Method)
                %TODO: Maybe it is good to save all the values as well
                Results{iRun}(iMethod).ItrAve(:,itr) = sum([agentItr{iMethod}(1:params{1}.numAgents).desireVec],2)./ params{1}.numAgents ;
            end
            
            
        elseif id1> params{1}.numAgents    % The selected agent is Ad agent
            
            rndForSharingIdea = rand;
            for iMethod= 1:length(Method)
                
                switch Method(iMethod)
                    case 1
                        U = U_rnd;
                    case 2
                        U = U_deg;
                    case 4
                        U = U_FinalOpt;
%                     case 5
%                         U = U_OldOpt;
                end % SWITCH
                
                %Select the other agent to interact with
                connectedAgs =  find(U(id1-params{1}.numAgents,:)) ;
                P_interaction = repmat( 1/length(find(U(id1-params{1}.numAgents,:))), 1, length( find( U(id1- params{1}.numAgents,:) ) ) );
                rollet = cumsum (P_interaction);
                tmp = find (rollet >= rand);
                id2 = connectedAgs(tmp(1));
                
                %Sharing the ideas
                if rndForSharingIdea<= matrixParams{1}.Alpha_agentAd
                    agentItr{iMethod}(id1).desireVec = agentItr{iMethod}(id1).desireVec;
                    agentItr{iMethod}(id2).desireVec = matrixParams{1}.Eps_agentAd*(M*agentItr{iMethod}(id2).desireVec) + (1- matrixParams{1}.Eps_agentAd) * (M*agentItr{iMethod}(id1).desireVec);
                    
                else
                    agentItr{iMethod}(id1).desireVec = agentItr{iMethod}(id1).desireVec;
                    agentItr{iMethod}(id2).desireVec = agentItr{iMethod}(id2).desireVec;
                    
                end % IF rand
                
                %%%%%%%%%%%%%%%%%%%%%%%%%%%
                %RESULT SAVE
                %%%%%%%%%%%%%%%%%%%%%%%%%%%
                %TODO: Maybe it is good to save all the values as well
                Results{iRun}(iMethod).ItrAve(:,itr) = sum([agentItr{iMethod}(1:params{1}.numAgents).desireVec],2)./ params{1}.numAgents ;

            end % FOR iMethod
        end %IF id1
        if false & mod(itr,1000) ==1
        subplot(2,2,1)
        image(255*[agentItr{1}(1:params{1}.numAgents).desireVec])
        colorbar
        title('random')
        subplot(2,2,2),
        image(255*[agentItr{2}(1:params{1}.numAgents).desireVec])
        colorbar
        title('degree')
                subplot(2,2,3)
        image(255*[agentItr{3}(1:params{1}.numAgents).desireVec])
        colorbar
        title('Hierarchical Optimal')
            subplot(2,2,4),
        image(255*[agentItr{4}(1:params{1}.numAgents).desireVec])
        colorbar
        title('Global optimal')

        pause(0.001)
        end;
        %     %TODO: Maybe it is good to save all the values as well
        %     Results(m).ItrAve(:,itr) = sum([agent{1}(1:params{1}.numAgents).desireVec],2)./ params{1}.numAgents ;
        
    end % FOR itr
    %%%%%%%%%%%%%%%%%%%%%%%%%%%
    %ITERATION ENDS
    %%%%%%%%%%%%%%%%%%%%%%%%%%%
    
    
    %%%%%%%%%%%%%%%%%%%%%%%%%%%
    %RESULT AVERAGING
    %%%%%%%%%%%%%%%%%%%%%%%%%%%
    for iMethod= 1:length(Method)
        %Calculate Average of Desire Vector of ALL AGENTS
        Results{iRun}(iMethod).FinalDesireVec = [agentItr{iMethod}(1:params{1}.numAgents).desireVec];
        Results{iRun}(iMethod).AveFinalDesireVec = sum([agentItr{iMethod}(1:params{1}.numAgents).desireVec],2)./ params{1}.numAgents ;
        
    end % FOR iMethod
end % FOR iRun

% Save each RUN results
%save (sprintf('Run%d_Agent%d_Ad%d',run,numInitAgents,numAds),'Results','Net','agent','Alpha_agentAd','Eps_agentAd','numAds','numAgents','numRun','numItr');
%
%
figure(1)
for iMethod = 1:length(Method)
    averageResults{iMethod} = zeros(params{1}.numAds,simParams.numItr);
    
    
    for iRun = 1 : simParams.numRun
        averageResults{iMethod} = averageResults{iMethod} + Results{iRun}(iMethod).ItrAve/simParams.numRun;
    end;
end;
%
figure(2)
% to plot
pl1= mean(averageResults{1});
pl2= mean(averageResults{2});
pl3= mean(averageResults{3});
% pl4= mean(averageResults{4});

%y1 = mean(pl1);
plot(1:100:simParams.numItr,pl1(1:100:end))
hold on

%y2 = mean(pl2);
plot(1:100:simParams.numItr,pl2(1:100:end),'r')

%y3 = mean(pl3);
plot(1:100:simParams.numItr,pl3(1:100:end),'g')

% y4 = mean(pl4);
% plot(1:100:simParams.numItr,pl4(1:100:end),'k')

% y5 = mean(pl5);
% plot(1:100:numItr,y5(1:100:end),'m')
%legend('Random','Degree','Short path','Betweenness','Optimized')
% legend('Random','Degree','Optimized','old Optimized')
legend('Random','Degree','Optimized')
hold off


%%%%% for debug
%         view(biograph(sparse(subMatrixParams.P_agents),[],'ShowArrows','off','ShowWeights','on'))
%
%         view(biograph(sparse(augmentedSubMatrixParams.P_agents),[],'ShowArrows','off','ShowWeights','on'))
%         pause
%       %%%%%

%%%%%%%% For Diff Methods
% switch Method(m)
%     case 1
%         U = U_rnd;
%         load agent
%         agent{1}=agent1 ;
%         %             for init = 1:params{1}.numAgents
%         %                 agent{1}(init).desireVec = 0.02 + zeros(numAds,1);
%         %             end
%     case 2
%         U = U_deg;
%         load agent
%         agent{1}=agent1 ;
%         %             for init = 1:numAgents
%         %                 agent{1}(init).desireVec = 0.02 + zeros(numAds,1);
%         %             end
%         %         case 3
%         %             U = U_bet;
%         %             for init = 1:numAgents
%         %                 agent{1}(init).desireVec = 0.02 + zeros(numAds,1);
%         %             end
%     case 4
%         U = U_FinalOpt;
%         load agent
%         agent{1}=agent1 ;
%         %             for init = 1:numAgents
%         %                 agent{1}(init).desireVec = 0.02 + zeros(numAds,1);
%         %             end
%     case 5
%         U = U_OldOpt;
%         load agent
%         agent{1}=agent1 ;
% end % SWITCH
